import json
import platform
import re
import time
import traceback

from VoltWatcherPack.ImageStitching import stitch_images_in_folder
from business.steering.steeringData import get_steering_info
import cv2
import logging
import os
from business.runTrajectory.runTrajectory import add_run_trajectory, close_run_trajectory
import VoltWatcherPack.VoltWatcher as Vw

from business.protocol import protocolData

# 测试数据
from business.storage import storageData

#
# logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s',
#                     filename=r'.\log\robot.log',  # 指定日志文件名
#                     filemode='w'  # 选择写入模式：'a'表示追加写入，'w'表示覆盖写入
#                     )

# 创建一个日志器
logger = logging.getLogger(__name__)


def extract_value(data_packet, data_type='int'):
    # 使用正则表达式提取任意标识符和数字部分
    match = re.search(r'\{[^:]+:(\d+)\}', data_packet)

    if match:
        value = match.group(1)
        if data_type == 'int':
            return int(value)
        # 可在此扩展，支持其他数据类型
    return None


# 定义过滤函数
def filter_by_key(data_list, key, value):
    return [item for item in data_list if item[key] == value]


# 定义提取位置信息函数
def extract_positions(filtered_data):
    return [json.loads(item['position']) for item in filtered_data]


# 定义处理位置信息函数
def process_positions(positions):
    return sorted(set(int(position['Y']) for position in positions))


def capture_image(output_dir, filename="image.jpg", duration=3, border_width=0):
    """
    持续捕获摄像头图像，持续duration秒后返回最后一张图像。
    """
    # 确保输出目录存在
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # 构建完整的输出文件路径
    output_file = os.path.join(output_dir, filename)

    # 打开摄像头，参数0表示第一个摄像头
    cap = cv2.VideoCapture(0)

    # 设置摄像头分辨率
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

    # 检查是否成功打开摄像头
    if not cap.isOpened():
        print("无法打开摄像头")
        return None

    start_time = time.time()
    frame = None

    # 持续捕获图像直到时间结束
    while time.time() - start_time < duration:
        ret, frame = cap.read()
        if not ret:
            print("未能读取到图像")
            break

    if frame is not None:
        # 居中裁剪图像
        original_width, original_height = 1920, 1080
        target_width, target_height = 1920, 1056
        x_start = (original_width - target_width) // 2
        y_start = (original_height - target_height) // 2
        frame = frame[y_start:y_start + target_height, x_start:x_start + target_width]

        # 获取图像的高度和宽度
        height, width, _ = frame.shape

        # 设置要覆盖的黑色区域宽度
        # 你可以根据需求调整这个值

        # 只有当 border_width > 0 时才加黑色边框
        if border_width > 0:
            frame[:, :border_width] = [255, 255, 255]  # 左侧黑色
            frame[:, -border_width:] = [255, 255, 255]  # 右侧黑色

        # 保存最后一张图像
        if cv2.imwrite(output_file, frame):
            print(f"图像已保存到 {output_file}")
        else:
            print("保存图像失败")

    # 释放摄像头资源
    cap.release()
    return frame


def map_value(input_value, input_min, input_max, output_min, output_max):
    # 确保输入值在输入范围内
    if input_value < input_min:
        input_value = input_min
    elif input_value > input_max:
        input_value = input_max

    # 使用线性插值公式
    output_value = output_min + (output_max - output_min) * (input_value - input_min) / (input_max - input_min)
    return output_value


pro_list = []


# 机器人运行
def robot_run(sto_list, task_id):
    from serialPort.serial_tool import Serial, run_until_response_outNum, run_until_response
    global pro_list, live_state
    if live_state:
        logging.info("实况任务进行中，取消本次执行！")
        return
    pro_list = []
    add_run_trajectory('start', 'start', 1, '0')

    # 获取指定配电柜编号的信息
    for sto in sto_list:
        robot_warehouse_key = '000'

        should_break = False  # 标志位
        for item in pro_list:
            if item['warehouseKey'] == sto['storage_key']:
                should_break = True  # 设置标志位为True
                for i in range(3):
                    state = storageData.external_data(item['listOutput'], sto['storage_key'], task_id, devTime)
                    if state:
                        break  # 跳出内层循环
                    time.sleep(3)
                if should_break:  # 检查标志位，如果为True则跳出外层循环
                    break
        if should_break:
            continue

        exit_flag = False
        while True:
            devTime = time.strftime("%Y%m%d%H%M%S", time.localtime())

            # 启动机器人，发送命令和监听应答
            received_data = run_until_response('{PM:RUN}', '{MM:OK}')

            logging.info("接收到启动应答\"{MM:OK}\"")

            # 一直监听，等待配电柜编号的数据
            """
            单句命令：  {MM:L:001:01}
            MM:单片机端
            L:直线 C为拐角
            001:位号1
            01:第一圈
            """
            received_data = run_until_response("Loding...", '{MM:*:*:*}')
            logging.info(f"接受到运行指令：{received_data}")

            # 提取单片机端信息
            mcu_data = re.search(r"\{MM:(\w):(\d+):(\d+)\}", received_data)

            if mcu_data:
                logging.info(
                    f"这是{mcu_data.group(1)}类二维码，位号：{mcu_data.group(2)}，圈数：{mcu_data.group(3)}")  # 输出：A 123 45
            if mcu_data.group(1) == 'C':
                turn_statue = get_steering_info(mcu_data.group(2), int(mcu_data.group(3)))
                logging.info(f"{turn_statue['id']}")
                logging.info(f"发送{turn_statue['steering_type']}方向给到小车")
                received_data = run_until_response(f"{{PT:{turn_statue['steering_type']}}}", '{MT:OK}')
                add_run_trajectory(mcu_data.group(1), mcu_data.group(2), mcu_data.group(3),
                                   turn_statue['steering_type'])
                continue
            if mcu_data.group(1) == 'L':
                num = int(mcu_data.group(2))
                add_run_trajectory(mcu_data.group(1), mcu_data.group(2), mcu_data.group(3),
                                   '2')
                robot_warehouse_key = f"{num:03d}"
            # 获取指定配电柜信息
            list_input = protocolData.get_dev_protocol(robot_warehouse_key)
            # 将输入数据复制一份作为输出数据的基础
            list_output = list_input.copy()

            # 获取指定仓库键的数据
            result = filter_by_key(list_input, 'warehouse_key', robot_warehouse_key)
            # 提取位置信息
            positions = extract_positions(result)

            x_wide = 0
            if len(positions) > 1:
                x_wide = int(positions[0]['X'])

            # 处理位置信息，提取Y值并排序
            sorted_y_values = process_positions(positions)
            layer_num = len(sorted_y_values)

            # 拍摄图像和拼接图像
            image_num = 16
            unit_height = 1000 // image_num
            for i in range(image_num):
                received_data = run_until_response_outNum(f"{{PH:{unit_height * i}}}", '{MH:OK}', timeoutNum=20)
                if received_data is None:
                    exit_flag = True
                    break
                image = capture_image(rf"file/picture/{robot_warehouse_key}/{devTime}/original_image",
                                      filename=f'{i:03d}.jpg',
                                      border_width=x_wide)
                logging.info(f"在 {unit_height * i} 高度成功拍摄图像")

            if exit_flag:
                break

            try:

                logging.info(f"正在拼接图像...")
                stitch_image = stitch_images_in_folder(rf"file/picture/{robot_warehouse_key}/{devTime}/original_image")
                cv2.imwrite(rf"file/picture/{robot_warehouse_key}/{devTime}/stitch_image.jpg", stitch_image)
                logging.info(f"成功拼接图像并保存!!!")

                logging.info(f"开始识别仪表!!!")
                meter_readings, detected_img = Vw.detect_and_process_meters(
                    stitch_image, r"file/moudle/Meter1920x4096.onnx", conf_thres=0.5, iou_thres=0.4
                )
                logging.info(f"识别仪表完成!!!")

                output_file = rf"file/picture/{robot_warehouse_key}_h/{devTime}/{robot_warehouse_key}.jpg"
                os.makedirs(os.path.dirname(output_file), exist_ok=True)
                if cv2.imwrite(output_file, detected_img):
                    logging.info(f"识别后的图像已保存到 {output_file}")


            except Exception as e:

                # 只记录一次错误信息，并且使用 exc_info 来自动包含堆栈跟踪

                logging.error(f"物体检测失败: {e}", exc_info=True)

                continue

            # 检查是否检测到了仪表
            if not meter_readings:
                logging.error(f"警告：在 {robot_warehouse_key} 处没有检测到任何仪表")
                continue

            # 更新输出数据中的仪表读数
            for i in range(len(meter_readings)):
                logging.info(f"第 {i} 块表的值为 {meter_readings[i]['angle']:.2f}")

                # 遍历输出数据列表，匹配位置和表号
                num = 1
                for item in list_output:
                    position_dict = json.loads(item['position'])
                    target_ptl_bm = f"B{num:03d}"
                    num += 1

                    # 基于Y值和ptl_bm匹配
                    if item['ptl_bm'] == target_ptl_bm:
                        input_value = meter_readings[i]['angle']
                        output_value = map_value(input_value, -10, 100, 0, 450)
                        logging.info(f"映射后的输出值为:{output_value:.2f}")
                        item['ptlValue'] = f"{output_value:.2f}"
                        logging.info(f"更新了 {item['ptl_bm']} 的值为 {item['ptlValue']}")
                        break

            if mcu_data.group(1) == 'L':
                num = int(mcu_data.group(2))
                logging.info(f"num：{num}")
                robot_warehouse_key = f"{num:03d}"
                cz_flag = False
                for item in pro_list:
                    if item['warehouseKey'] == robot_warehouse_key:
                        cz_flag = True
                if cz_flag is False:
                    pro_list.append({
                        'warehouseKey': robot_warehouse_key,
                        'listOutput': list_output
                    })
                send_flag = False
                if robot_warehouse_key == sto['storage_key']:
                    send_flag = True
                    for i in range(3):
                        state = storageData.external_data(list_output, sto['storage_key'], task_id, devTime)
                        if state:
                            break
                        time.sleep(3)
                if send_flag:
                    logging.info(f"{sto['storage_key']}识别完成！")
                    break
    logging.info(f"定时任务执行完成: {len(pro_list)} 个识别到！")
    logging.info(f"等待设备发送结束指令！")
    received_data = run_until_response('{PM:RUN}', '{MM:OK}')
    while True:
        received_data = run_until_response("Loding...", '{MM:*:*:*}')

        # 提取单片机端信息
        mcu_data = re.search(r"\{MM:(\w):(\d+):(\d+)\}", received_data)

        if mcu_data:
            logging.info(
                f"这是{mcu_data.group(1)}类二维码，位号：{mcu_data.group(2)}，圈数：{mcu_data.group(3)}")  # 输出：A 123 45

        if mcu_data.group(1) == 'C':
            turn_statue = get_steering_info(mcu_data.group(2), int(mcu_data.group(3)))
            logging.info(f"{turn_statue['id']}")
            logging.info(f"发送{turn_statue['steering_type']}方向给到小车")
            received_data = run_until_response(f"{{PT:{turn_statue['steering_type']}}}", '{MT:OK}')
            received_data = run_until_response('{PM:RUN}', '{MM:OK}')
            add_run_trajectory(mcu_data.group(1), mcu_data.group(2), mcu_data.group(3), turn_statue['steering_type'])
            logging.info("接收到启动应答\"{MM:OK}\"")

        elif mcu_data.group(1) == 'L':
            received_data = run_until_response('{PH:0}', '{MH:OK}')
            received_data = run_until_response('{PM:RUN}', '{MM:OK}')
            add_run_trajectory(mcu_data.group(1), mcu_data.group(2), mcu_data.group(3),
                               '2')
        elif mcu_data.group(1) == 'S':
            logging.info(f"任务结束")
            close_run_trajectory()
            return


live_pro_list = []
live_state = False


# 机器人运行
def robot_run_live(storage_key):
    from serialPort.serial_tool import Serial, run_until_response, run_until_response_outNum
    global live_pro_list, live_state
    # 初始化串口并设置参数

    warehouse_key = storage_key
    live_state = True
    robot_warehouse_key = '000'
    if storage_key == 'start':
        live_pro_list.clear()
        add_run_trajectory('start', 'start', 1, '0')
        return []
    if storage_key == 'end':
        received_data = run_until_response('{PM:RUN}', '{MM:OK}')
        while True:
            received_data = run_until_response("Loding...", '{MM:*:*:*}')

            # 提取单片机端信息
            mcu_data = re.search(r"\{MM:(\w):(\d+):(\d+)\}", received_data)

            if mcu_data:
                logging.info(f"这是{mcu_data.group(1)}类二维码，位号：{mcu_data.group(2)}，圈数：{mcu_data.group(3)}")  # 输出：A 123 45

            if mcu_data.group(1) == 'C':
                turn_statue = get_steering_info(mcu_data.group(2), int(mcu_data.group(3)))
                logging.info(f"{turn_statue['id']}")
                logging.info(f"发送{turn_statue['steering_type']}方向给到小车")
                received_data = run_until_response(f"{{PT:{turn_statue['steering_type']}}}", '{MT:OK}')
                received_data = run_until_response('{PM:RUN}', '{MM:OK}')
                add_run_trajectory(mcu_data.group(1), mcu_data.group(2), mcu_data.group(3),
                                   turn_statue['steering_type'])
                logging.info("接收到启动应答\"{MM:OK}\"")

            elif mcu_data.group(1) == 'L':
                received_data = run_until_response('{PH:0}', '{MH:OK}')
                received_data = run_until_response('{PM:RUN}', '{MM:OK}')
                add_run_trajectory(mcu_data.group(1), mcu_data.group(2), mcu_data.group(3),
                                   '2')
            elif mcu_data.group(1) == 'S':
                live_state = False
                logging.info(f"任务结束")
                close_run_trajectory()
                return

    for item in live_pro_list:
        if item['warehouseKey'] == warehouse_key:
            return item['listOutput']

    while True:
        devTime = time.strftime("%Y%m%d%H%M%S", time.localtime())

        # 启动机器人，发送命令和监听应答
        received_data = run_until_response('{PM:RUN}', '{MM:OK}')

        logging.info("接收到启动应答\"{MM:OK}\"")

        # 一直监听，等待配电柜编号的数据
        """
        单句命令：  {MM:L:001:01}
        MM:单片机端
        L:直线 C为拐角
        001:位号1
        01:第一圈
        """
        received_data = run_until_response("Loding...", '{MM:*:*:*}')
        logging.info(f"接受到运行指令：{received_data}")

        # 提取单片机端信息
        mcu_data = re.search(r"\{MM:(\w):(\d+):(\d+)\}", received_data)

        if mcu_data:
            logging.info(
                f"这是{mcu_data.group(1)}类二维码，位号：{mcu_data.group(2)}，圈数：{mcu_data.group(3)}")  # 输出：A 123 45

        if mcu_data.group(1) == 'C':
            turn_statue = get_steering_info(mcu_data.group(2), int(mcu_data.group(3)))
            logging.info(f"{turn_statue['id']}")
            logging.info(f"发送{turn_statue['steering_type']}方向给到小车")
            received_data = run_until_response(f"{{PT:{turn_statue['steering_type']}}}", '{MT:OK}')
            add_run_trajectory(mcu_data.group(1), mcu_data.group(2), mcu_data.group(3), turn_statue['steering_type'])
            continue
        if mcu_data.group(1) == 'L':
            num = int(mcu_data.group(2))
            add_run_trajectory(mcu_data.group(1), mcu_data.group(2), mcu_data.group(3),
                               '2')
            robot_warehouse_key = f"{num:03d}"
        # 获取指定配电柜信息
        list_input = protocolData.get_dev_protocol(robot_warehouse_key)
        # 将输入数据复制一份作为输出数据的基础
        list_output = list_input.copy()

        # 获取指定仓库键的数据
        result = filter_by_key(list_input, 'warehouse_key', robot_warehouse_key)
        # 提取位置信息
        positions = extract_positions(result)

        x_wide = 0
        if len(positions) > 1:
            x_wide = int(positions[0]['X'])

        # 处理位置信息，提取Y值并排序
        sorted_y_values = process_positions(positions)
        layer_num = len(sorted_y_values)

        # 拍摄图像和拼接图像
        image_num = 16
        unit_height = 1000 // image_num
        for i in range(image_num):
            received_data = run_until_response_outNum(f"{{PH:{unit_height * i}}}", '{MH:OK}', timeoutNum=20)
            if received_data is None:
                return []
            image = capture_image(rf"file/picture/{robot_warehouse_key}/{devTime}/original_image",
                                  filename=f'{i:03d}.jpg',
                                  border_width=x_wide)
            logging.info(f"在 {unit_height * i} 高度成功拍摄图像")

        try:
            logging.info(f"正在拼接图像...")
            stitch_image = stitch_images_in_folder(rf"file/picture/{robot_warehouse_key}/{devTime}/original_image")
            cv2.imwrite(rf"file/picture/{robot_warehouse_key}/{devTime}/stitch_image.jpg", stitch_image)
            logging.info(f"成功拼接图像并保存!!!")

            logging.info(f"开始识别仪表!!!")
            meter_readings, detected_img = Vw.detect_and_process_meters(
                stitch_image, r"file/moudle/Meter1920x4096.onnx", conf_thres=0.3, iou_thres=0.4
            )

            output_file = rf"file/picture/{robot_warehouse_key}_h/{devTime}/{robot_warehouse_key}.jpg"
            os.makedirs(os.path.dirname(output_file), exist_ok=True)
            if cv2.imwrite(output_file, detected_img):
                logging.info(f"识别后的图像已保存到 {output_file}")



        except Exception as e:

            # 只记录一次错误信息，并且使用 exc_info 来自动包含堆栈跟踪

            logging.error(f"物体检测失败: {e}", exc_info=True)

            continue

        # 检查是否检测到了仪表
        if not meter_readings:
            logging.error(f"警告：在 {robot_warehouse_key} 处没有检测到任何仪表")
            continue

        # 更新输出数据中的仪表读数
        for i in range(len(meter_readings)):
            logging.info(f"第 {i} 块表的值为 {meter_readings[i]['angle']:.2f}")

            # 遍历输出数据列表，匹配位置和表号
            num = 1
            for item in list_output:
                position_dict = json.loads(item['position'])
                target_ptl_bm = f"B{num:03d}"
                num += 1

                # 基于Y值和ptl_bm匹配
                if item['ptl_bm'] == target_ptl_bm:
                    input_value = meter_readings[i]['angle']
                    output_value = map_value(input_value, -10, 100, 0, 450)
                    logging.info(f"映射后的输出值为:{output_value:.2f}")
                    item['ptlValue'] = f"{output_value:.2f}"
                    logging.info(f"更新了 {item['ptl_bm']} 的值为 {item['ptlValue']}")
                    break

        if mcu_data.group(1) == 'L':
            num = int(mcu_data.group(2))
            logging.info(f"num：{num}")
            robot_warehouse_key = f"{num:03d}"
            if robot_warehouse_key == warehouse_key:
                return list_output
            else:
                cz_flag = False
                for item in live_pro_list:
                    if item['warehouseKey'] == robot_warehouse_key:
                        cz_flag = True
                if cz_flag is False:
                    live_pro_list.append({
                        'warehouseKey': robot_warehouse_key,
                        'listOutput': list_output
                    })
